/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.commonmodel.core.session.sessionconfig;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.inspur.edp.commonmodel.core.session.serviceinterface.SessionConfigService;
import com.inspur.edp.commonmodel.core.session.tableentity.ConnectInfo;
import com.inspur.edp.commonmodel.core.session.tableentity.SessionConfig;
import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.tenancy.api.context.MultiTenantContextInfo;
import io.iec.edp.caf.tenancy.core.context.MultiTenantContextHolder;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

public class SessionConfigServiceImpl implements SessionConfigService {

    private static final ConcurrentHashMap<String, SessionConfig> configInfo = new ConcurrentHashMap<>();
    private final BefRTConfigRepository repository;
    private static Logger logger = LoggerFactory.getLogger(SessionConfigServiceImpl.class);

    public SessionConfigServiceImpl(BefRTConfigRepository repository){
        this.repository = repository;
    }

    @Override
    public SessionConfig getSessionConfig() {
        String app = CAFContext.current.getAppCode();
        String su = CAFContext.current.getCurrentSU();
        if (StringUtils.isEmpty(app) || StringUtils.isEmpty(su)) {
            logger.error("获取当前应用实例与SU的信息失败！");
            return null;
        }
        String key = app + "." + su;
        SessionConfig existing = configInfo.get(key);
        if (existing != null) {
            return existing;
        }

        try {
            connectMasterDb();
            SessionConfig config = null;
            try {
                config = repository.getSessionConfigByBelongAppInstanceAndBelongSU(app, su);
                if (config == null) {
                    config = repository.getSessionConfigByBelongAppInstanceAndBelongSU(app, "*");
                }
            } catch (Exception e) {
                //兼容无config表
                logger.error("获取sessionCache失败,sessionConfig表不存在！", e);
            }
            //兼容未预置数据或预置数据错误
            if (config == null) {
                logger.warn("未预置实例：" + app + "下su：" + su + "的sessionCacheConfig,将使用默认设置");
                config = buildDefaultConfig(app, su);
                configInfo.put(key, config);
                return config;
            }
            config.setConnectInfo(DeSerializeConnectInfo(config.getConfigInfo()));
            if (config.getConnectInfo() == null) {
                logger.error("实例：" + app + "下su：" + su + "的相关sessionCacheConfig配置不完整,将使用默认设置");
                config = buildDefaultConfig(app, su);
                configInfo.put(key, config);
                return config;
            }
            configInfo.put(key, config);
            return config;
        } finally {
            MultiTenantContextHolder.set(null);
        }
    }

    @Override
    public void saveSessionConfig(SessionConfig config){
        try {
            connectMasterDb();
            config.setConfigInfo(SerializeConnectInfo(config.getConnectInfo()));
            repository.save(config);
        }finally {
            MultiTenantContextHolder.set(null);
        }
    }

    @Override
    public void deleteSessionConfig(String id){
        try {
            connectMasterDb();
            if (id == null || "".equals(id))
                return;
            if (configInfo.containsKey(id)) {
                configInfo.remove(id);
            }
            SessionConfig config = repository.findById(id).orElse(null);
            if (config != null)
                repository.deleteById(id);
        }finally {
            MultiTenantContextHolder.set(null);
        }
    }

    @Override
    public void updateConfigCache(SessionConfig config){
        configInfo.put(config.getBelongAppInstance()+"."+config.getBelongSU(), config);
    }

    @Override
    public List<SessionConfig> getAllConfig() {
        try {
            connectMasterDb();
            List<SessionConfig> configs = repository.findAll();
            for (SessionConfig config : configs) {
                config.setConnectInfo(DeSerializeConnectInfo(config.getConfigInfo()));
            }
            return configs;
        }finally {
            MultiTenantContextHolder.set(null);
        }
    }

    private SessionConfig buildDefaultConfig(String app,String su){
        SessionConfig config = new SessionConfig();
        config.setId("default");
        config.setBelongAppInstance(app);
        config.setBelongSU(su);
        ConnectInfo connectInfo = new ConnectInfo();
        connectInfo.setUseCache(false);
        config.setConnectInfo(connectInfo);
        return config;
    }

    private String SerializeConnectInfo(ConnectInfo info){
        try {
            String value = null;
            if(info!=null) {
                value = new ObjectMapper().writeValueAsString(info);
            }
            return value;
        } catch (JsonProcessingException | RuntimeException e) {
            throw new RuntimeException("ConnectInfo序列化失败",e);
        }
    }

    private ConnectInfo DeSerializeConnectInfo(String info) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            ConnectInfo vm = mapper.readValue(info, ConnectInfo.class);
            return vm;
        } catch (IOException e) {
            throw new RuntimeException("ConnectInfo反序列化失败",e);
        }
    }

    private void connectMasterDb(){
        MultiTenantContextInfo contextInfo = new MultiTenantContextInfo();
        contextInfo.setMasterDb(true);
        MultiTenantContextHolder.set(contextInfo);
    }

}
